package com.sy.rebbitmq_spring.config;

import com.rabbitmq.client.Channel;
import com.sy.convert.consume.MessageDelegate;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.amqp.support.ConsumerTagStrategy;
import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;

import java.util.HashMap;
import java.util.UUID;

@Configuration
@ComponentScan({"com.sy.rebbitmq_spring.*"})
public class RabbitMQConfig {
    @Value("${rabbiitmq.host}")
    private String host;
    @Value("${rabbiitmq.port}")
    private int port;
    @Value("${rabbiitmq.username}")
    private String username;
    @Value("${rabbiitmq.password}")
    private String password;

    /**
     * @Bean("name") 注入名称必须要和获取rabbitAdmin的入参名称一致(没有尝试过初始化方式)
     * @return
     */
    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost("/");
        return connectionFactory;
    }

    /**
     * 注入RabbitAdmin对象
     * @param connectionFactory 入参名称必须要和上面注入对象名称一致
     * @return
     */
//    @Bean("rabbitAdmin")
    @Bean()
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        //运行服务
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
    private String queue001Name="queue001";
    private String topic001Name="topic001";
    private String queue002Name="queue002";
    private String topic002Name="topic002";
    /**
     * 可以直接用@Bean()的方式将exchange和queue以及binding直接注入到容器中RabbitmqAdmin会再启动时从容器中获取
     */
    @Bean
    public TopicExchange exchange001(){
        return new TopicExchange(topic001Name,true,false);
    }

    /**
     * 可以直接用@Bean()的方式将exchange和queue以及binding直接注入到容器中RabbitmqAdmin会再启动时从容器中获取
     */
    @Bean
    public TopicExchange exchange002(){
        return new TopicExchange(topic002Name,true,false);
    }
    @Bean
    public Queue queue001(){
        return new Queue(queue001Name,false);
    }
    @Bean
    public Queue queue002(){
        return new Queue(queue002Name,false);
    }
    @Bean
    public Binding binding001() {
        String routingKey = "spring.*";
        //对象调用链
//        return BindingBuilder.bind(queue001()).to(exchange001()).with(routingKey);
        return new Binding(
                queue001Name,
                Binding.DestinationType.QUEUE,
                topic001Name,
                routingKey, null
        );
    }

    @Bean
    public Binding binding002() {
        String routingKey = "spring2.*";
        //对象调用链
//        return BindingBuilder.bind(queue001()).to(exchange001()).with(routingKey);
        return new Binding(
                queue002Name,
                Binding.DestinationType.QUEUE,
                topic002Name,
                routingKey, null
        );
    }
    @Bean
    public RabbitTemplate getRabbitTemplate(ConnectionFactory connectionFactory){
        return new RabbitTemplate(connectionFactory);
    }

  /*  *//**
     * 使用 simplemessagelistenercontainer 消息监听容器
     * 监听队列和解析消息为对象
     * @param connectionFactory
     * @return
     *//*
    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
        SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
        //监听多个队列
        simpleMessageListenerContainer.setQueues(queue001(),queue002());
        //设置消费者数量
        simpleMessageListenerContainer.setConcurrentConsumers(1);
        //设置最多并发消费者
        simpleMessageListenerContainer.setMaxConcurrentConsumers(5);
        //是否重回队列
        simpleMessageListenerContainer.setDefaultRequeueRejected(false);
        //设置签收模式
        simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费者标签(类似id)策略 用来反查消费者
        simpleMessageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                System.out.println("设置消费者标签");
                return queue+"_"+ UUID.randomUUID().toString();
            }
        });
        //设置消费者
*//*        simpleMessageListenerContainer.setMessageListener(new ChannelAwareMessageListener() {
            //单条消息消费
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                String messageStr = new String(message.getBody());
                System.out.println("收到消息");
                System.out.println(messageStr);
            }
        });*//*


// todo 第一种方式 适配器方式 绑定以上队列的消费者调用方法
//        //设置消息监听适配器
//        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
//                new MessageDelegate()
//        );
//        //默认调用就是handleMessage(byte[] msg)方法
//        messageListenerAdapter.setDefaultListenerMethod("handleMessage2");
//        //将参数转换 可以转换为java对象或者java对象转字符串
//        // 在上面的消息监听器中直接方法入参为转换后的参数
//        messageListenerAdapter.setMessageConverter(new TestMessageConverter());

//todo 第二种方式 适配器方式 让队列名称和消费者调用方法名称匹配
        //设置消息监听适配器
        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
                new MessageDelegate()
        );
        HashMap<String, String> map = new HashMap<>();
        map.put("queue001","handleMessage1");
        map.put("queue002","handleMessage2");
        messageListenerAdapter.setQueueOrTagToMethodName(map);
        //转换消息参数类型
        messageListenerAdapter.setMessageConverter(new TestMessageConverter());
        //消息监听容器设置监听适配器
        simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
        return simpleMessageListenerContainer;

    }*/

    private String converter_queue_01="converter_queue_01";
    private String converter_topic_01="converter_topic_01";
    @Bean
    public TopicExchange converter_topic_01(){
        return new TopicExchange(converter_topic_01,true,false);
    }
    @Bean
    public Queue converter_queue_01(){
        return new Queue(converter_queue_01,false);
    }
    @Bean
    public Binding converter_queue_bind() {
        String routingKey = "spring.*";
        //对象调用链
        return BindingBuilder.bind(converter_queue_01()).to(converter_topic_01()).with(routingKey);
    }

    @Bean
    public Binding converter_queue_bind2() {
        String routingKey = "spring2.*";
        //对象调用链
        return BindingBuilder.bind(converter_queue_01()).to(converter_topic_01()).with(routingKey);
    }
    /**
     * 使用 simplemessagelistenercontainer 消息监听容器
     * 监听队列和解析消息为对象
     * @param connectionFactory
     * @return
     */
    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
        SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
        //监听多个队列
        simpleMessageListenerContainer.setQueues(converter_queue_01());
        //设置消费者数量
        simpleMessageListenerContainer.setConcurrentConsumers(1);
        //设置最多并发消费者
        simpleMessageListenerContainer.setMaxConcurrentConsumers(5);
        //是否重回队列
        simpleMessageListenerContainer.setDefaultRequeueRejected(false);
        //设置签收模式
        simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费者标签(类似id)策略 用来反查消费者
        simpleMessageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                System.out.println("设置消费者标签");
                return queue+"_"+ UUID.randomUUID().toString();
            }
        });


        // todo 第一种方式 适配器方式 绑定以上队列的消费者调用方法
        //        //设置消息监听适配器
        //        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
        //                new MessageDelegate()
        //        );
        //        //默认调用就是handleMessage(byte[] msg)方法
        //        messageListenerAdapter.setDefaultListenerMethod("handleMessage2");
        //        //将参数转换 可以转换为java对象或者java对象转字符串
        //        // 在上面的消息监听器中直接方法入参为转换后的参数
        //        messageListenerAdapter.setMessageConverter(new TestMessageConverter());

//        //todo 第二种方式 适配器方式 让队列名称和消费者调用方法名称匹配 json方式
//        //设置消息监听适配器
//        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
//                new MessageDelegate()
//        );
//        HashMap<String, String> map = new HashMap<>();
//        map.put("converter_queue_01","handleMessage3");
//        messageListenerAdapter.setQueueOrTagToMethodName(map);
//        //1. 设置json格式的转换器
//        Jackson2JsonMessageConverter jsonMessageConverter = new Jackson2JsonMessageConverter();
//
//        //转换消息参数类型
//        messageListenerAdapter.setMessageConverter(jsonMessageConverter);
//        //消息监听容器设置监听适配器
//        simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
//        return simpleMessageListenerContainer;

//        //todo 转换为java对象方式
//        //设置消息监听适配器
//        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
//                new MessageDelegate()
//        );
//        HashMap<String, String> map = new HashMap<>();
//        map.put("converter_queue_01","handleMessage3");
//        messageListenerAdapter.setQueueOrTagToMethodName(map);
//        //1. 设置json格式的转换器
//        Jackson2JsonMessageConverter jsonMessageConverter = new Jackson2JsonMessageConverter();
//        //2.根据生产端 给消息头设置的__TypeId__ 指定的全路径的类转换 并且调用 handleMessage3函数中相同入参类型的处理方法(重载)
//        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
//        jsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
//        //转换消息参数类型
//        messageListenerAdapter.setMessageConverter(jsonMessageConverter);
//        //消息监听容器设置监听适配器
//        simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
//        return simpleMessageListenerContainer;


        //todo 转换为java对象方式 多映射和设置别名转换
        //设置消息监听适配器
        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
                new MessageDelegate()
        );
        HashMap<String, String> map = new HashMap<>();
        map.put("converter_queue_01","handleMessage3");
        messageListenerAdapter.setQueueOrTagToMethodName(map);
        //1. 设置json格式的转换器
        Jackson2JsonMessageConverter jsonMessageConverter = new Jackson2JsonMessageConverter();
        //2.根据生产端 给消息头设置的__TypeId__ 指定的全路径的类转换 并且调用 handleMessage3函数中相同入参类型的处理方法(重载)
        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
        jsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
        //3.设置别名和类的映射对象
        HashMap<String, Class<?>> nickNameClassHashMap = new HashMap<>();
        nickNameClassHashMap.put("order",com.sy.convert.beans.Order.class);
        nickNameClassHashMap.put("packaged",com.sy.convert.beans.Packaged.class);
        javaTypeMapper.setIdClassMapping(nickNameClassHashMap);


        //转换消息参数类型
        messageListenerAdapter.setMessageConverter(jsonMessageConverter);
        //消息监听容器设置监听适配器
        simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
        return simpleMessageListenerContainer;

    }
}
